package com.hube.platform.tenant.helper;

import com.codahale.metrics.*;
import com.hube.platform.tenant.configuration.DynamicDataSource;
import com.hube.platform.tenant.constant.AppConstant;
import com.hube.platform.tenant.constant.StatusEnum;
import com.hube.platform.tenant.constant.StringPool;
import com.hube.platform.tenant.entity.vo.DatabaseMonitorVO;
import com.zaxxer.hikari.HikariDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;

/**
 * @author : Aaron
 * @date : 2022/6/13
 */
public class HikariCPHelper {
    private static final Logger log = LoggerFactory.getLogger(HikariCPHelper.class);

    public static List<DatabaseMonitorVO> monitor() {
        List<DatabaseMonitorVO> result = new ArrayList<>();
        long time = System.currentTimeMillis();
        //获取多数据源里面，所有的poolName
        Map<Object, Object> targetDataSources = DynamicDataSource.getTargetDataSources();
        for (Map.Entry<Object, Object> entry : targetDataSources.entrySet()) {
            String tenantId = (String) entry.getKey();
            DatabaseMonitorVO vo = new DatabaseMonitorVO(tenantId, time);
            try {
                HikariDataSource dataSource = (HikariDataSource) entry.getValue();
                MetricRegistry metricRegistry = (MetricRegistry) (dataSource).getMetricRegistry();
                SortedMap<String, Gauge> gauges = metricRegistry.getGauges();
                //参数1:活跃连接数，此数据长期保持最大连接数值的时候可以尝试扩大连接数
                Gauge<Integer> activeConnections = gauges.get(generateIndexName(tenantId, "ActiveConnections"));
                Integer activeConnectionsVal = activeConnections.getValue();
                vo.setActiveConnections(activeConnectionsVal);
                //参数2：空闲连接数，此数据过高的时候可以尝试减少配置中的最小连接数
                Gauge<Integer> IdleConnections = gauges.get(generateIndexName(tenantId, "IdleConnections"));
                Integer idleConnectionsVal = IdleConnections.getValue();
                vo.setIdleConnections(idleConnectionsVal);
                //参数3：配置的最大连接数
                Gauge<Integer> MaxConnections = gauges.get(generateIndexName(tenantId, "MaxConnections"));
                Integer maxConnectionsVal = MaxConnections.getValue();
                vo.setMaxConnections(maxConnectionsVal);
                //参数4：配置的最小连接数
                Gauge<Integer> MinConnections = gauges.get(generateIndexName(tenantId, "MinConnections"));
                Integer minConnectionsVal = MinConnections.getValue();
                vo.setMinConnections(minConnectionsVal);
                //参数5：排队等待连接的线程数，如果此数据持续飙高，表示连接池中已经没有空闲线程了
                Gauge<Integer> PendingConnections = gauges.get(generateIndexName(tenantId, "PendingConnections"));
                Integer pendingConnectionsVal = PendingConnections.getValue();
                vo.setPendingConnections(pendingConnectionsVal);
                //参数6：当前总连接数
                Gauge<Integer> TotalConnections = gauges.get(generateIndexName(tenantId, "TotalConnections"));
                Integer totalConnectionsVal = TotalConnections.getValue();
                vo.setTotalConnections(totalConnectionsVal);
                //参数7：创建新连接的耗时，此数据主要反应当前服务到数据服务的网络延迟
                SortedMap<String, Histogram> histograms = metricRegistry.getHistograms();
                Histogram ConnectionCreation = histograms.get(generateIndexName(tenantId, "ConnectionCreation"));
                Long connectionCreationV = ConnectionCreation.getCount();
                vo.setConnectionCreation(connectionCreationV);
                //参数8：连接被复用时长，此参数表示连接池中一个连接从返回连接池到再次被复用的时间间隔，表示数据访问频繁程度，对于使用较长的间隔可以尝试减少连接数
                Histogram Usage = histograms.get(generateIndexName(tenantId, "Usage"));
                Long usageVal = Usage.getCount();
                vo.setUsage(usageVal);
                //参数9：创建新连接的超时，如果经常创建连接超时这个时候需要排查数据服务或者网络通讯是否异常
                SortedMap<String, Meter> meters = metricRegistry.getMeters();
                Meter meter = meters.get(generateIndexName(tenantId, "ConnectionTimeoutRate"));
                Long timeoutCount = meter.getCount();
                vo.setConnectionTimeoutRate(timeoutCount);
                //参数10：获取连接的等待耗时，可以和PendingConnections结合分析连接池情况。
                SortedMap<String, Timer> timers = metricRegistry.getTimers();
                Timer timer = timers.get(generateIndexName(tenantId, "Wait"));
                Long waitCount = timer.getCount();
                vo.setWait(waitCount);
                result.add(vo);
            } catch (Exception e) {
                vo.setTenantId(tenantId);
                vo.setStatus(StatusEnum.DISABLE.code());
                vo.setRemarks(e.getLocalizedMessage());
                result.add(vo);
                log.error("----连接池监控异常----：{}，{}", tenantId, e);
                continue;
            }
        }
        return result;
    }

    /**
     * 创建数据库连接池名称
     *
     * @param tenantId
     * @return
     */
    public static String generatePoolName(String tenantId) {
        return AppConstant.DYNAMIC_DATA_SOURCE + StringPool.DASH + tenantId;
    }

    /**
     * 获取 MetricRegistry 监控指标名
     *
     * @param key
     * @return
     */
    private static String generateIndexName(String tenantId, String key) {
        StringBuffer buffer = new StringBuffer();
        return buffer.append(generatePoolName(tenantId)).append(".pool.").append(key).toString();
    }
}
